package com.gframework.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.lang.Nullable;

/**
 * 反射操作工具类
 * 
 * @since 1.0.0
 * @author Ghwolf
 */
public class ReflectUtils {

	/**
	 * 一个空的type类型数组
	 */
	private static final Type[] EMPTY_TYPE = new Type[0];

	private ReflectUtils() {
	}

	/**
	 * 尝试寻找一个类型指定父类类型或接口类型（会一直想上寻找）的指定泛型类型的指定索引位置上的泛型的具体类型.
	 * <p>
	 * 此方法可以找到如下几种情况下的泛型具体类型：
	 * <ul>
	 * <li>1、直接实现接口或类并直接指定具体泛型类型</li>
	 * <li>2、通过子接口或子类指定的泛型并继承到了特定类型上。如下</li>
	 * </ul>
	 * 
	 * <pre>
	 * A{@code <E>} extends ArrayList{@code <E>} {}
	 * B extends A{@code <String>} {}
	 * </pre>
	 * </p>
	 * 未指定泛型类型，则会返回Object.class。
	 * 
	 * @param sourceClass 原始类型，将从此类型开始寻找
	 * @param findClass 要寻找的接口类型或父类类型
	 * @param typeNum 返回第几个泛型参数位置上的泛型具体类型，从0开始计数
	 * @return 如果存在则返回，否则返回null，如果存在但是没有指定具体泛型类型，则返回Object的class类型
	 * @throws NullPointerException 参数为null
	 * @throws IllegalArgumentException
	 *             参数错误，如typeNum小于0，findClass不存在泛型，或者typeNum大于findClass的泛型最大索引。
	 */
	public static Class<?> getParameterizedClass(Class<?> sourceClass, Class<?> findClass, int typeNum) {
		if (typeNum < 0) {
			throw new IllegalArgumentException("typeNum 不能小于0");
		}
		int length = findClass.getTypeParameters().length;
		if (length == 0) {
			throw new IllegalArgumentException(findClass.getName() + " 类型没有泛型参数！");
		}
		if (typeNum >= length) {
			throw new IllegalArgumentException(
					findClass.getName() + " 类型只有" + length + "个泛型参数，但是typeNum却是：" + typeNum + "，泛型参数索引从0开始。");
		}

		return getParameterizedClass0(sourceClass, findClass, typeNum);
	}
	
	/**
	 * 取得指定类型指定接口或父类反省类型，不做参数检查
	 */
	private static Class<?> getParameterizedClass0(Class<?> sourceClass, Class<?> findClass, int typeNum) {
		if (sourceClass == findClass) {
			return Object.class;
		}
		Type[] types = getTypesByClass(sourceClass, findClass);
		if (types == null) {
			return null;
		} else if (types.length == 0) {
			return Object.class;
		}
		
		Type t = types[typeNum];
		if (t instanceof Class) {
			return (Class<?>) t;
		} else if (t instanceof ParameterizedType) {
			Type classT = ((ParameterizedType) t).getRawType();
			if (classT instanceof Class) {
				return (Class<?>) classT;
			} else {
				throw new GReflectUtilsFrameworkException("ReflectUtils#getParameterizedClass() getRawType()返回结果不是class类型："
						+ classT.getClass() + " -> " + findClass.getName());
			}
		} else if (t instanceof TypeVariable) {
			Class<?> nextFindClass = (Class<?>) ((TypeVariable<?>) t).getGenericDeclaration();
			int n = findTypeVarIndex(nextFindClass,t);
			return getParameterizedClass0(sourceClass, nextFindClass, n);
		} else if (t instanceof GenericArrayType) {
			Class<?> nextFindClass = (Class<?>) ((TypeVariable<?>) ((GenericArrayType) t).getGenericComponentType())
					.getGenericDeclaration();
			int n = findTypeVarIndex(nextFindClass,t);
			return getParameterizedClass0(sourceClass, nextFindClass, n);
		} else {
			throw new GReflectUtilsFrameworkException("ReflectUtils#getParameterizedClass() 未能解析的type类型：" + t.getClass() + " -> "
					+ findClass.getName());
		}
	}

	/**
	 * 返回一个类的指定【直接继承的】父类或接口的泛型类型，由于泛型有多个，所以以数组方式返回，如果没有任何泛型类型，或明确指定，则数组长度为0.
	 * <p>
	 * 如果希望一直向上寻找，可以使用{@link #getTypesByClass(Class, Class)}方法。
	 * </p>
	 * <p>
	 * 如果你觉得手动再去解析复杂的Type类型非常麻烦，你可以调用 方法来直接取得泛型类型的直接class类型。
	 * </p>
	 * 
	 * @param sourceClass 要判断的原始类型
	 * @param findClass 要取得的接口或父类的类型，必须是直接继承的类或接口类型
	 * @return 如果不存在特定类型的父类或接口，则返回null，否则返回Type数组，如果存在类型但是没有泛型类型，则数组长度为0
	 * @throws NullPointerException 参数为null
	 */
	@Nullable
	public static Type[] getTypesByGenericClass(Class<?> sourceClass, Class<?> findClass) {
		if (!findClass.isAssignableFrom(sourceClass)) {
			return null;
		}
		Type t = null;
		if (findClass.isInterface()) {
			Class<?>[] clses = sourceClass.getInterfaces();
			int foot = 0;
			for (Class<?> cls : clses) {
				if (cls == findClass) {
					t = sourceClass.getGenericInterfaces()[foot];
					break;
				}
				foot ++;
			}
		} else {
			t = sourceClass.getGenericSuperclass();
		}

		return t == null ? null : getParameterizedTypes(t);
	}

	/**
	 * 返回一个类的指定父类或接口的泛型类型，由于泛型有多个，所以以数组方式返回，如果没有任何泛型类型，或明确指定，则数组长度为0.<br>
	 * 此方法和{@link #getTypesByGenericClass(Class, Class)}方法不同之处在于，他会递归的向上寻找，直到找到头或找到为止。
	 * 
	 * @param sourceClass 要判断的原始类型
	 * @param findClass 要取得的接口或父类的类型
	 * @return 如果不存在特定类型的父类或接口，则返回null，否则返回Type数组，如果存在类型但是没有泛型类型，则数组长度为0
	 * @throws NullPointerException 参数为null
	 */
	@Nullable
	public static Type[] getTypesByClass(Class<?> sourceClass, Class<?> findClass) {
		if (!findClass.isAssignableFrom(sourceClass)) {
			return null;
		}
		if (findClass.isInterface()) {
			Class<?>[] inters = sourceClass.getInterfaces();
			for (Class<?> inter : inters) {
				if (inter == findClass) {
					return getTypesByGenericClass(sourceClass, findClass);
				} else if (findClass.isAssignableFrom(inter)) {
					return getTypesByClass(inter, findClass);
				}
			}
			Class<?> superClass = sourceClass.getSuperclass();
			if (superClass != null && superClass != Object.class) {
				return getTypesByClass(superClass, findClass);
			}
		} else {
			Class<?> thisClass = sourceClass;
			Class<?> superClass = thisClass.getSuperclass();
			while (superClass != null && superClass != Object.class) {
				if (superClass == findClass) {
					return getParameterizedTypes(thisClass.getGenericSuperclass());
				}
				thisClass = superClass;
				superClass = superClass.getSuperclass();
			}
		}

		return null;
	}

	/**
	 * 取得一个type对象的泛型类型，如果没有，则数组长度为0
	 * 
	 * @param type type类型
	 * @return 泛型类型数组，如果没有，则数组长度为0
	 */
	private static Type[] getParameterizedTypes(Type type) {
		if (type instanceof ParameterizedType) {
			return ((ParameterizedType) type).getActualTypeArguments();
		} else {
			return EMPTY_TYPE;
		}
	}
	
	/**
	 * 查询指定类型上的指定泛型索引，如果不存在返回{@code -1}
	 */
	private static int findTypeVarIndex(Class<?> cls,Type type) {
		int foot = 0 ;
		for (Type t : cls.getTypeParameters()) {
			if (t == type) return foot ;
			foot ++;
		}
		return -1;
	}

	/**
	 * 查询具有指定注解的所有属性，包括父类.
	 * 如果参数为null或没有找到任何属性，则集合长度为0
	 * 
	 * @param obj 要查询的对象
	 * @param anno 要匹配的注解
	 * @return 返回属性集合
	 */
	public static List<Field> findFieldsByAnno(Object obj, Class<? extends Annotation> anno) {
		if (obj == null || anno == null) return Collections.emptyList();
		List<Field> list = new ArrayList<>();
		Class<?> cls = obj.getClass();
		while (cls != Object.class) {
			Field[] fs = cls.getDeclaredFields();
			for (int x = 0; x < fs.length; x ++) {
				Field f = fs[x];
				if (f.getAnnotation(anno) != null) {
					list.add(f);
				}
			}
			cls = cls.getSuperclass();
		}
		return list;
	}

	/**
	 * 查询具有指定注解的所有方法，包括父类.
	 * 如果参数为null或没有找到任何属性，则集合长度为0
	 * 
	 * @param obj 要查询的对象
	 * @param anno 要匹配的注解
	 * @return 返回方法集合
	 */
	public static List<Method> findMethodsByAnno(Object obj, Class<? extends Annotation> anno) {
		if (obj == null || anno == null) return Collections.emptyList();
		List<Method> list = new ArrayList<>();
		Class<?> cls = obj.getClass();
		while (cls != Object.class) {
			Method[] ms = cls.getDeclaredMethods();
			for (int x = 0; x < ms.length; x ++) {
				Method m = ms[x];
				if (m.getAnnotation(anno) != null) {
					list.add(m);
				}
			}
			cls = cls.getSuperclass();
		}
		return list;
	}

	/**
	 * 尝试寻找一个属性的公开getter方法
	 * 
	 * @param field 成员属性对象
	 * @return 如果存在返回方法对象，否则返回null
	 */
	public static Method getFieldGetterMethod(Field field) {
		return getFieldGetterMethod(field, false);
	}

	/**
	 * 尝试寻找一个属性的getter方法
	 * 
	 * @param field 成员属性对象
	 * @param access 是否查找非公开方法,true表示查找非公开方法
	 * @return 返回方法集合
	 */
	public static Method getFieldGetterMethod(Field field, boolean access) {
		if (field == null) return null;
		String methodName = "get" + GStringUtils.capitalize(field.getName());
		Class<?> cls = field.getDeclaringClass();
		if (access) {
			while (cls != Object.class) {
				Method[] ms = cls.getDeclaredMethods();
				for (int x = 0; x < ms.length; x ++) {
					Method m = ms[x];
					if (m.getName().equals(methodName) && m.getParameterCount() == 0
							&& m.getReturnType() == field.getType()) {
						return m;
					}
				}
				cls = cls.getSuperclass();
			}
			return null;
		} else {
			Method[] ms = cls.getMethods();
			for (int x = 0; x < ms.length; x ++) {
				if (ms[x].getName().equals(methodName)) {
					return ms[x];
				}
			}
			return null;
		}
	}
	
	/**
	 * 判断一个接口类型是否是函数式接口.
	 * 
	 * @param cls 要判断的类型
	 * @return 如果是函数式接口返回true，否则返回false，如果cls为null返回false
	 */
	public static boolean isFunctionInterface(@Nullable Class<?> cls) {
		if (cls == null || !cls.isInterface()) return false ;
		boolean flag = cls.getDeclaredAnnotation(FunctionalInterface.class) != null;
		if (flag) return true ;
		
		int abstractMethods = 0 ;
		for (Method m : cls.getMethods()) {
			if (Modifier.isAbstract(m.getModifiers())) {
				if (abstractMethods == 1) {
					return false ;
				}
				abstractMethods ++ ;
			}
		}
		return abstractMethods == 1;
	}
	
	/**
	 * 取得函数式接口的默认方法对象.
	 * 
	 * @param cls 要获取的接口类型
	 * @return 如果是函数式接口，则返回默认方法，否则返回null
	 */
	public static Method getFunctionInterfaceMethod(@Nullable Class<?> cls) {
		if (isFunctionInterface(cls)) {
			for (Method m : cls.getDeclaredMethods()) {
				if (Modifier.isAbstract(m.getModifiers())) {
					return m ;
				}
			}
			// Ignore 
			throw new UnsupportedOperationException();
		} else {
			return null ;
		}
	}
	
	/**
	 * 本方法可以判断一个类是否包含有指定名称的成员属性(非static).
	 * <p>本类不会检查父类，如果需要检查父类，可以使用{@link #hasField(Class, String, boolean)}属性
	 * @param cls 要检查的类，可以为null
	 * @param fieldName 属性名称，可以为null
	 * @return 如果包含则返回true，否则返回false
	 */
	public static boolean hasField(@Nullable Class<?> cls,@Nullable String fieldName) {
		if (cls == null || fieldName == null) return false ;
		
		for (Field f : cls.getDeclaredFields()) {
			if (Modifier.isStatic(f.getModifiers())) {
				continue ;
			}
			if (f.getName().equals(fieldName)) {
				return true ;
			}
		}
		return false ;
	}
	/**
	 * 本方法可以判断一个类是否包含有指定名称的成员属性(非static).
	 * 
	 * @param cls 要检查的类，可以为null
	 * @param fieldName 属性名称，可以为null
	 * @param findParent 是否检查父类属性
	 * @return 如果包含则返回true，否则返回false
	 */
	public static boolean hasField(Class<?> cls,String fieldName,boolean findParent) {
		if (!findParent) {
			return hasField(cls,fieldName);
		}
		if (cls == null || fieldName == null) return false ;
		
		Class<?> currentCls = cls;
		while(currentCls != Object.class) {
			for (Field f : currentCls.getDeclaredFields()) {
				if (Modifier.isStatic(f.getModifiers())) {
					continue ;
				}
				if (f.getName().equals(fieldName)) {
					return true ;
				}
			}
			currentCls = currentCls.getSuperclass();
		}
		return false ;
		
	}
	
	/**
	 * 框架自身异常，如果出现，则需要联系开发者对工具代码进行升级。
	 */
	@SuppressWarnings("serial")
	private static class GReflectUtilsFrameworkException extends RuntimeException {
		private static final String MSG_PREFIX = "【GReflectUtils框架异常，请联系开发者】";
		public GReflectUtilsFrameworkException(String msg) {
			super(MSG_PREFIX + msg);
		}
	}
}
